/* ***************************************************************************
 *
 * internal/compiler-macros.h
 *
 * Compiler-specific macro definitions used internally by FreeType.
 *
 * Copyright (C) 2020-2021 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef INTERNAL_COMPILER_MACROS_H_
#define INTERNAL_COMPILER_MACROS_H_

#include <freetype/config/public-macros.h>

FT_BEGIN_HEADER

/* Fix compiler warning with sgi compiler. */
#if defined(__sgi) && !defined(__GNUC__)
#if defined(_COMPILER_VERSION) && (_COMPILER_VERSION >= 730)
#pragma set woff 3505
#endif
#endif

/* Fix compiler warning with sgi compiler. */
#if defined(__sgi) && !defined(__GNUC__)
#if defined(_COMPILER_VERSION) && (_COMPILER_VERSION >= 730)
#pragma set woff 3505
#endif
#endif

/*
 * When defining a macro that expands to a non-trivial C statement, use
 * FT_BEGIN_STMNT and FT_END_STMNT to enclose the macro's body.  This
 * ensures there are no surprises when the macro is invoked in conditional
 * branches.
 *
 * Example:
 *
 * #define  LOG( ... )        \
 * FT_BEGIN_STMNT           \
 * if ( logging_enabled ) \
 * log( __VA_ARGS__ );  \
 * FT_END_STMNT
 */
#define FT_BEGIN_STMNT do {
#define FT_END_STMNT \
    }                \
    while (0)

/*
 * FT_DUMMY_STMNT expands to an empty C statement.  Useful for
 * conditionally defined statement macros.
 *
 * Example:
 *
 * #ifdef BUILD_CONFIG_LOGGING
 * #define  LOG( ... )         \
 * FT_BEGIN_STMNT           \
 * if ( logging_enabled ) \
 * log( __VA_ARGS__ );  \
 * FT_END_STMNT
 * #else
 * #  define LOG( ... )  FT_DUMMY_STMNT
 * #endif
 */
#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT

#ifdef __UINTPTR_TYPE__
/*
 * GCC and Clang both provide a `__UINTPTR_TYPE__` that can be used to
 * avoid a dependency on `stdint.h`.
 */
#define FT_UINT_TO_POINTER(x) (void *)(__UINTPTR_TYPE__)(x)
#elif defined(_WIN64)
/* only 64bit Windows uses the LLP64 data model, i.e., */
/* 32-bit integers, 64-bit pointers.                   */
#define FT_UINT_TO_POINTER(x) (void *)(unsigned __int64)(x)
#else
#define FT_UINT_TO_POINTER(x) (void *)(unsigned long)(x)
#endif

/*
 * Use `FT_TYPEOF( type )` to cast a value to `type`.  This is useful to
 * suppress signedness compilation warnings in macros.
 *
 * Example:
 *
 * #define PAD_( x, n )  ( (x) & ~FT_TYPEOF( x )( (n) - 1 ) )
 *
 * (The `typeof` condition is taken from gnulib's `intprops.h` header
 * file.)
 */
#if ((defined(__GNUC__) && __GNUC__ >= 2) || (defined(__IBMC__) && __IBMC__ >= 1210 && defined(__IBM__TYPEOF__)) || \
    (defined(__SUNPRO_C) && __SUNPRO_C >= 0x5110 && !__STDC__))
#define FT_TYPEOF(type) (__typeof__(type))
#else
#define FT_TYPEOF(type) /* empty */
#endif

/*
 * Mark a function declaration as internal to the library.  This ensures
 * that it will not be exposed by default to client code, and helps
 * generate smaller and faster code on ELF-based platforms.  Place this
 * before a function declaration.
 */

/* Visual C, mingw */
#if defined(_WIN32)
#define FT_INTERNAL_FUNCTION_ATTRIBUTE /* empty */

/* gcc, clang */
#elif (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__)
#define FT_INTERNAL_FUNCTION_ATTRIBUTE __attribute__((visibility("hidden")))

/* Sun */
#elif defined(__SUNPRO_C) && __SUNPRO_C >= 0x550
#define FT_INTERNAL_FUNCTION_ATTRIBUTE __hidden

#else
#define FT_INTERNAL_FUNCTION_ATTRIBUTE /* empty */
#endif

/*
 * FreeType supports compilation of its C sources with a C++ compiler (in
 * C++ mode); this introduces a number of subtle issues.
 *
 * The main one is that a C++ function declaration and its definition must
 * have the same 'linkage'.  Because all FreeType headers declare their
 * functions with C linkage (i.e., within an `extern "C" { ... }` block
 * due to the magic of FT_BEGIN_HEADER and FT_END_HEADER), their
 * definition in FreeType sources should also be prefixed with `extern
 * "C"` when compiled in C++ mode.
 *
 * The `FT_FUNCTION_DECLARATION` and `FT_FUNCTION_DEFINITION` macros are
 * provided to deal with this case, as well as `FT_CALLBACK_DEF` and its
 * siblings below.
 */

/*
 * `FT_FUNCTION_DECLARATION( type )` can be used to write a C function
 * declaration to ensure it will have C linkage when the library is built
 * with a C++ compiler.  The parameter is the function's return type, so a
 * declaration would look like
 *
 * FT_FUNCTION_DECLARATION( int )
 * foo( int x );
 *
 * NOTE: This requires that all uses are inside of `FT_BEGIN_HEADER ...
 * FT_END_HEADER` blocks, which guarantees that the declarations have C
 * linkage when the headers are included by C++ sources.
 *
 * NOTE: Do not use directly.  Use `FT_LOCAL`, `FT_BASE`, and `FT_EXPORT`
 * instead.
 */
#define FT_FUNCTION_DECLARATION(x) extern x

/*
 * Same as `FT_FUNCTION_DECLARATION`, but for function definitions instead.
 *
 * NOTE: Do not use directly.  Use `FT_LOCAL_DEF`, `FT_BASE_DEF`, and
 * `FT_EXPORT_DEF` instead.
 */
#ifdef __cplusplus
#define FT_FUNCTION_DEFINITION(x) extern "C" x
#else
#define FT_FUNCTION_DEFINITION(x) x
#endif

/*
 * Use `FT_LOCAL` and `FT_LOCAL_DEF` to declare and define, respectively,
 * an internal FreeType function that is only used by the sources of a
 * single `src/module/` directory.  This ensures that the functions are
 * turned into static ones at build time, resulting in smaller and faster
 * code.
 */
#ifdef FT_MAKE_OPTION_SINGLE_OBJECT

#define FT_LOCAL(x) static x
#define FT_LOCAL_DEF(x) static x

#else

#define FT_LOCAL(x)                \
    FT_INTERNAL_FUNCTION_ATTRIBUTE \
    FT_FUNCTION_DECLARATION(x)
#define FT_LOCAL_DEF(x) FT_FUNCTION_DEFINITION(x)

#endif
/* FT_MAKE_OPTION_SINGLE_OBJECT */

/*
 * Use `FT_LOCAL_ARRAY` and `FT_LOCAL_ARRAY_DEF` to declare and define,
 * respectively, a constant array that must be accessed from several
 * sources in the same `src/module/` sub-directory, and which are internal
 * to the library.
 */
#define FT_LOCAL_ARRAY(x)          \
    FT_INTERNAL_FUNCTION_ATTRIBUTE \
    extern const x
#define FT_LOCAL_ARRAY_DEF(x) FT_FUNCTION_DEFINITION(const x)

/*
 * `Use FT_BASE` and `FT_BASE_DEF` to declare and define, respectively, an
 * internal library function that is used by more than a single module.
 */
#define FT_BASE(x)                 \
    FT_INTERNAL_FUNCTION_ATTRIBUTE \
    FT_FUNCTION_DECLARATION(x)
#define FT_BASE_DEF(x) FT_FUNCTION_DEFINITION(x)


/*
 * NOTE: Conditionally define `FT_EXPORT_VAR` due to its definition in
 * `src/smooth/ftgrays.h` to make the header more portable.
 */
#ifndef FT_EXPORT_VAR
#define FT_EXPORT_VAR(x) FT_FUNCTION_DECLARATION(x)
#endif

/*
 * When compiling FreeType as a DLL or DSO with hidden visibility,
 * some systems/compilers need a special attribute in front OR after
 * the return type of function declarations.
 *
 * Two macros are used within the FreeType source code to define
 * exported library functions: `FT_EXPORT` and `FT_EXPORT_DEF`.
 *
 * - `FT_EXPORT( return_type )`
 *
 * is used in a function declaration, as in
 *
 * ```
 * FT_EXPORT( FT_Error )
 * FT_Init_FreeType( FT_Library*  alibrary );
 * ```
 *
 * - `FT_EXPORT_DEF( return_type )`
 *
 * is used in a function definition, as in
 *
 * ```
 * FT_EXPORT_DEF( FT_Error )
 * FT_Init_FreeType( FT_Library*  alibrary )
 * {
 * ... some code ...
 * return FT_Err_Ok;
 * }
 * ```
 *
 * You can provide your own implementation of `FT_EXPORT` and
 * `FT_EXPORT_DEF` here if you want.
 *
 * To export a variable, use `FT_EXPORT_VAR`.
 */

/* See `freetype/config/compiler_macros.h` for the `FT_EXPORT` definition */
#define FT_EXPORT_DEF(x) FT_FUNCTION_DEFINITION(x)

/*
 * The following macros are needed to compile the library with a
 * C++ compiler and with 16bit compilers.
 */

/*
 * This is special.  Within C++, you must specify `extern "C"` for
 * functions which are used via function pointers, and you also
 * must do that for structures which contain function pointers to
 * assure C linkage -- it's not possible to have (local) anonymous
 * functions which are accessed by (global) function pointers.
 *
 *
 * FT_CALLBACK_DEF is used to _define_ a callback function,
 * located in the same source code file as the structure that uses
 * it.  FT_COMPARE_DEF, in addition, ensures the `cdecl` calling
 * convention on x86, required by the C library function `qsort`.
 *
 * FT_BASE_CALLBACK and FT_BASE_CALLBACK_DEF are used to declare
 * and define a callback function, respectively, in a similar way
 * as FT_BASE and FT_BASE_DEF work.
 *
 * FT_CALLBACK_TABLE is used to _declare_ a constant variable that
 * contains pointers to callback functions.
 *
 * FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable
 * that contains pointers to callback functions.
 *
 *
 * Some 16bit compilers have to redefine these macros to insert
 * the infamous `_cdecl` or `__fastcall` declarations.
 */
#ifdef __cplusplus
#define FT_CALLBACK_DEF(x) extern "C" x
#else
#define FT_CALLBACK_DEF(x) static x
#endif

#if defined(__i386__)
#define FT_COMPARE_DEF(x) FT_CALLBACK_DEF(x) __attribute__((cdecl))
#elif defined(_M_IX86)
#define FT_COMPARE_DEF(x) FT_CALLBACK_DEF(x) __cdecl
#else
#define FT_COMPARE_DEF(x) FT_CALLBACK_DEF(x)
#endif

#define FT_BASE_CALLBACK(x) FT_FUNCTION_DECLARATION(x)
#define FT_BASE_CALLBACK_DEF(x) FT_FUNCTION_DEFINITION(x)

#ifndef FT_CALLBACK_TABLE
#ifdef __cplusplus
#define FT_CALLBACK_TABLE extern "C"
#define FT_CALLBACK_TABLE_DEF extern "C"
#else
#define FT_CALLBACK_TABLE extern
#define FT_CALLBACK_TABLE_DEF /* nothing */
#endif
#endif
/* FT_CALLBACK_TABLE */

FT_END_HEADER

#endif
/* INTERNAL_COMPILER_MACROS_H_ */
